package com.example.easyexcel.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.example.easyexcel.aop.monitor.ExecutionTimeMonitor;
import com.example.easyexcel.aop.monitor.MonitorConstants;
import com.example.easyexcel.dao.AssetTargetInfoMapper;
import com.example.easyexcel.dto.AssetTargetInfo;
import com.example.easyexcel.listener.AssetTargetInfoAopListener;
import com.example.easyexcel.service.AssetTargetInfoAopService;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@RequiredArgsConstructor
@Slf4j
public class AssetTargetInfoServiceAopImpl implements AssetTargetInfoAopService {

    private final AssetTargetInfoMapper assetTargetInfoMapper;

    // 配置常量
    private static final int CORE_POOL_MULTIPLIER = 2;
    private static final int MAX_POOL_MULTIPLIER = 4;
    private static final int QUEUE_CAPACITY = 1000;

    // 线程池
    private final ThreadPoolExecutor sheetExecutor = createThreadPool();

    private ThreadPoolExecutor createThreadPool() {
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        int corePoolSize = availableProcessors * CORE_POOL_MULTIPLIER;
        int maxPoolSize = availableProcessors * MAX_POOL_MULTIPLIER;

        return new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadFactory() {
                    private final AtomicInteger threadNumber = new AtomicInteger(1);
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "excel-processor-" + threadNumber.getAndIncrement());
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    @Override
    @ExecutionTimeMonitor(value = MonitorConstants.EXCEL_IMPORT_TOTAL, logParameters = true)
    public void importTargetExcel(MultipartFile file) {
        Path tempFile = createTempFile(file);
        try {
            int sheetCount = getSheetCount(tempFile.toString());
            processAllSheets(tempFile.toString(), sheetCount);
        } finally {
            cleanupTempFile(tempFile);
        }
    }

    @ExecutionTimeMonitor(MonitorConstants.TEMP_FILE_CREATION)
    private Path createTempFile(MultipartFile file) {
        try {
            Path tempFile = Files.createTempFile("excel-import-", ".xlsx");
            file.transferTo(tempFile);
            return tempFile;
        } catch (Exception e) {
            throw new RuntimeException("创建临时文件失败", e);
        }
    }

    @ExecutionTimeMonitor(MonitorConstants.SHEET_COUNT_GET)
    private int getSheetCount(String filePath) {
        try (ExcelReader excelReader = EasyExcel.read(filePath).build()) {
            return excelReader.excelExecutor().sheetList().size();
        } catch (Exception e) {
            log.warn("获取sheet数量失败，默认使用1个sheet", e);
            return 1;
        }
    }

    @ExecutionTimeMonitor("处理所有Sheet")
    private void processAllSheets(String filePath, int sheetCount) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (int i = 0; i < sheetCount; i++) {
            int sheetIndex = i;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                processSingleSheet(filePath, sheetIndex);
            }, sheetExecutor);
            futures.add(future);
        }

        waitForAllFutures(futures);
    }

    @ExecutionTimeMonitor(value = MonitorConstants.SHEET_PROCESSING, logParameters = true)
    private void processSingleSheet(String filePath, int sheetIndex) {
        try (InputStream inputStream = new FileInputStream(filePath)) {
            AssetTargetInfoAopListener listener = new AssetTargetInfoAopListener(assetTargetInfoMapper);
            EasyExcel.read(inputStream, AssetTargetInfo.class, listener)
                    .sheet(sheetIndex)
                    .doRead();
        } catch (Exception e) {
            log.error("Sheet[{}] 处理失败", sheetIndex, e);
            throw new RuntimeException("Sheet处理失败", e);
        }
    }

    private void waitForAllFutures(List<CompletableFuture<Void>> futures) {
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .get(30, TimeUnit.MINUTES);
        } catch (TimeoutException e) {
            throw new RuntimeException("处理Excel超时", e);
        } catch (Exception e) {
            throw new RuntimeException("等待异步任务完成时出错", e);
        }
    }

    @ExecutionTimeMonitor("清理临时文件")
    private void cleanupTempFile(Path tempFile) {
        if (tempFile != null) {
            try {
                Files.deleteIfExists(tempFile);
            } catch (Exception e) {
                log.warn("删除临时文件失败: {}", e.getMessage());
            }
        }
    }

    @PreDestroy
    @ExecutionTimeMonitor("关闭线程池")
    public void shutdownExecutors() {
        sheetExecutor.shutdown();
        try {
            if (!sheetExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                List<Runnable> notExecutedTasks = sheetExecutor.shutdownNow();
                log.warn("线程池强制关闭，{}个任务未被执行", notExecutedTasks.size());
            }
        } catch (InterruptedException e) {
            sheetExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}